/*
Package toolutilv4 提供了一组通用切片函数，
函数目录：
  - AppendHead(AppendHeadInt,AppendHeadStr,AppendHeadSliceMap) 在切片头部追加一个整数元素，并返回新的切片
  - Contains(ContainsInt,ContainStr) 检查给定的切片中是否存在某个元素
  - ConvertSliceToMap 将结构体切片转出[]map[string]interface{}
  - FindDiffVales(FindDiffValesInt,FindDiffValesStr) 找到两个切片中不同的元素，返回两个切片中不同元素组成的切片
  - FindSameVales(FindSameValesInt,FindSameValesStr) 找到两个切片中同同的元素，返回两个切片中相同同元素组成的切片
  - FindOutVales(FindOutValesInt,FindOutValesStr) 从slice1里剔除slice2存在的值
  - FilterSlice 模糊搜索
  - GroupByKey 将[]map[string]interface{}按某个字段分组
  - MapListKeyToSlice(MapListKeyToSliceInt, MapListKeyToSliceStr) 从输入的 []map[string]interface{} 类型的切片中提取指定 key 对应的类型数值，并返回一个对应类型的切片
  - MoveToFirst 移动第一位
  - Paginate 分页
  - RemoveKeysFromMaps 删除指定key
  - Unique(UniqueInt, UniqueString) 切片去重
  - SortByKey 函数按照指定的 key 对应的值，对输入的 []map[string]interface{} 类型的切片进行排序。
  - StringToSlice(StringToSliceInt, StringToSliceStr) 将字符串解析为对应切片类型
*/
package toolutilv4

import (
	"encoding/json"
	"fmt"
	"reflect"
	"sort"
	"strconv"
	"strings"
)

var ToolSlice = toolSliceUtil{}

type toolSliceUtil struct{}

// AppendHeadInt 在切片头部追加一个整数元素，并返回新的切片
func (s *toolSliceUtil) AppendHeadInt(slice []int, addData int) []int {
	return append([]int{addData}, slice...)
}

// AppendHeadStr 在切片头部追加一个整数元素，并返回新的切片
func (s *toolSliceUtil) AppendHeadStr(slice []string, addData string) []string {
	return append([]string{addData}, slice...)
}

// AppendHeadSliceMap 在切片头部追加一个整数元素，并返回新的切片
func (s *toolSliceUtil) AppendHeadSliceMap(slice []map[string]interface{}, addData map[string]interface{}) []map[string]interface{} {
	// 创建一个新的切片，容量为原切片长度加1
	newSlice := make([]map[string]interface{}, len(slice)+1)
	// 在新切片的开头添加 addData 元素
	newSlice[0] = addData
	// 将原切片的元素拷贝到新切片中，从索引1开始，覆盖掉原切片的位置
	copy(newSlice[1:], slice)
	// 返回新的切片
	return newSlice
}

// ContainsInt 检查给定的切片中是否存在某个元素
func (s *toolSliceUtil) ContainsInt(slice []int, item int) bool {
	// 遍历整个切片
	for i := 0; i < len(slice); i++ {
		// 比较切片中的元素和要查找的元素
		if slice[i] == item {
			return true
		}
	}
	// 如果循环结束仍未找到匹配项，则返回false
	return false
}

// ContainStr 方法可以检查给定的切片中是否存在某个元素
func (s *toolSliceUtil) ContainStr(slice []string, item string) bool {
	// 遍历整个切片
	for i := 0; i < len(slice); i++ {
		// 比较切片中的元素和要查找的元素
		if slice[i] == item {
			return true
		}
	}
	// 如果循环结束仍未找到匹配项，则返回false
	return false
}

/*
   该函数接受一个参数 slice，可以接受任何类型的切片结构体。
   在函数内部，我们首先使用反射获取传入参数的值和类型，然后遍历每个结构体实例，将其转换为 map[string]interface{} 类型，并添加到结果切片中。最后，返回转换好的切片。
   如果传入的参数不是切片类型或者切片元素不是结构体类型，函数会返回一个错误 not a slice 或者 not a struct。这可以防止代码在运行时出现崩溃或者异常情况。
*/ // ConvertSliceToMap 将结构体切片转出[]map[string]interface{}
func (s *toolSliceUtil) ConvertSliceToMap(slice interface{}) []map[string]interface{} {
	// 获取slice的值
	sliceValue := reflect.ValueOf(slice)
	// 检查slice是否为切片类型
	if sliceValue.Kind() != reflect.Slice {
		return []map[string]interface{}{}
	}
	// 创建一个空的map切片，用于存储转换后的结果
	var result []map[string]interface{}

	// 遍历整个slice
	for i := 0; i < sliceValue.Len(); i++ {
		// 获取当前元素
		item := sliceValue.Index(i)
		// 获取当前元素的值
		itemValue := reflect.ValueOf(item.Interface())
		// 检查当前元素是否为结构体类型
		if itemValue.Kind() != reflect.Struct {
			return []map[string]interface{}{}
		}
		// 创建一个空的map，用于存储当前元素的字段名和对应的值
		itemMap := make(map[string]interface{})
		// 遍历当前元素的所有字段
		for j := 0; j < itemValue.NumField(); j++ {
			// 获取当前字段的名称
			fieldName := itemValue.Type().Field(j).Name
			// 获取当前字段的值
			fieldValue := itemValue.Field(j).Interface()
			// 将字段名和对应的值添加到map中
			itemMap[fieldName] = fieldValue
		}
		// 将当前元素转换为map后，将其添加到结果切片中
		result = append(result, itemMap)
	}
	return result
}

// FindDiffValesInt 找到两个切片中不同的元素，返回两个切片中不同元素组成的切片
func (s *toolSliceUtil) FindDiffValesInt(slice1 []int, slice2 []int) []int {
	// 创建一个空的切片，用于存储不同的元素
	diffVals := make([]int, 0)

	// 使用map存储第一个切片中的元素，以便快速查找
	lookup := make(map[int]struct{})
	for _, val := range slice1 {
		lookup[val] = struct{}{}
	}

	// 遍历第二个切片
	for _, val := range slice2 {
		// 如果第二个切片中的元素不在第一个切片中，则添加到不同值切片中
		if _, ok := lookup[val]; !ok {
			diffVals = append(diffVals, val)
		}
	}

	// 返回两个切片中不同元素组成的切片
	return diffVals
}

// FindDiffValesStr找到两个切片中不同的元素，返回两个切片中不同元素组成的切片
func (s *toolSliceUtil) FindDiffValesStr(slice1 []string, slice2 []string) []string {
	// 创建一个空的切片，用于存储不同的元素
	diffVals := make([]string, 0)

	// 使用map存储第一个切片中的元素，以便快速查找
	lookup := make(map[string]struct{})
	for _, val := range slice1 {
		lookup[val] = struct{}{}
	}

	// 遍历第二个切片
	for _, val := range slice2 {
		// 如果第二个切片中的元素不在第一个切片中，则添加到不同值切片中
		if _, ok := lookup[val]; !ok {
			diffVals = append(diffVals, val)
		}
	}

	// 返回两个切片中不同元素组成的切片
	return diffVals
}

// FindSameValesInt 找到两个切片中同同的元素，返回两个切片中相同同元素组成的切片
func (s *toolSliceUtil) FindSameValesInt(slice1 []int, slice2 []int) []int {
	sameVals := make([]int, 0)

	// 使用map存储第一个切片中的元素，以便快速查找
	lookup := make(map[int]struct{})
	for _, val := range slice1 {
		lookup[val] = struct{}{}
	}

	// 遍历第二个切片
	for _, val := range slice2 {
		// 如果第二个切片中的元素在第一个切片中，则添加到相同值切片中
		if _, ok := lookup[val]; ok {
			sameVals = append(sameVals, val)
		}
	}

	// 返回两个切片中相同元素组成的切片
	return sameVals
}

// FindSameValesStr 找到两个切片中同同的元素，返回两个切片中相同同元素组成的切片
func (s *toolSliceUtil) FindSameValesStr(slice1 []string, slice2 []string) []string {
	sameVals := make([]string, 0)

	// 使用map存储第一个切片中的元素，以便快速查找
	lookup := make(map[string]struct{})
	for _, val := range slice1 {
		lookup[val] = struct{}{}
	}

	// 遍历第二个切片
	for _, val := range slice2 {
		// 如果第二个切片中的元素在第一个切片中，则添加到相同值切片中
		if _, ok := lookup[val]; ok {
			sameVals = append(sameVals, val)
		}
	}

	// 返回两个切片中相同元素组成的切片
	return sameVals
}

func (s *toolSliceUtil) FindSameValesOfInt(slice1 []int, slice2 []int) []int {
	sameVales := make([]int, 0)

	// 遍历第一个切片
	for _, val1 := range slice1 {
		// 在第二个切片中查找相同值
		for _, val2 := range slice2 {
			if val1 == val2 {
				sameVales = append(sameVales, val1)
				break
			}
		}
	}
	return sameVales
}

// FindOutValesInt 从slice1里剔除slice2存在的值
func (s *toolSliceUtil) FindOutValesInt(slice1 []int, slice2 []int) []int {
	// 创建一个map，用于存储slice2中出现的所有元素
	m := make(map[int]bool)
	for _, i := range slice2 {
		m[i] = true
	}

	// 创建一个空的切片，用于存储slice1中不同于slice2的元素
	var result []int
	for _, i := range slice1 {
		if !m[i] {
			result = append(result, i)
		}
	}

	// 返回slice2独有的元素组成的切片
	return result
}

// FindOutValesStr 从slice1里剔除slice2存在的值
func (s *toolSliceUtil) FindOutValesStr(slice1 []string, slice2 []string) []string {
	// 创建一个map，用于存储slice2中出现的所有元素
	m := make(map[string]bool)
	for _, i := range slice2 {
		m[i] = true
	}

	// 创建一个空的切片，用于存储slice1中不同于slice2的元素
	var result []string
	for _, i := range slice1 {
		if !m[i] {
			result = append(result, i)
		}
	}

	// 返回slice2独有的元素组成的切片
	return result
}

// 模糊搜索
func (s *toolSliceUtil) FilterSlice(slice []map[string]interface{}, key string, value interface{}) []map[string]interface{} {
	result := make([]map[string]interface{}, 0)
	for _, item := range slice {
		if val, ok := item[key]; ok && strings.Contains(fmt.Sprintf("%v", val), fmt.Sprintf("%v", value)) {
			result = append(result, item)
		}
	}
	return result
}

/*
   参数 slice 是一个 []map[string]interface{} 类型的切片，每个 map 中包含一条数据记录。
   参数 key 和 name 都是字符串类型，分别表示按照哪个键进行分组和新生成的记录的名称。
    返回值是一个 []map[string]interface{} 类型的切片，其中每个 map 表示一组已经分好组的数据记录。
*/ // GroupByKey 将[]map[string]interface{}按某个字段分组
func (s *toolSliceUtil) GroupByKey(slice []map[string]interface{}, key string, name string) []map[string]interface{} {
	// 创建一个 map[interface{}][]interface{} 类型的变量 transformedData，用于存储已经分组后的数据。
	transformedData := make(map[interface{}][]interface{})
	// 遍历输入的每个 map[string]interface{} 类型的数据记录 d，在 transformedData 中找到对应的键 k，将其添加到分组后的列表中。
	for _, d := range slice {
		k := d[key]
		if data, ok := transformedData[k]; ok {
			transformedData[k] = append(data, d)
		} else {
			transformedData[k] = []interface{}{d}
		}
	}
	// 创建一个 []map[string]interface{} 类型的切片 result，遍历 transformedData map 中的所有键值对，
	// 并将其转换为新的 map[string]interface{} 类型的数据记录，并添加到 result 切片中。
	result := make([]map[string]interface{}, 0, len(transformedData))
	for k, v := range transformedData {
		m := map[string]interface{}{
			key:  k,
			name: v,
		}
		result = append(result, m)
	}
	// 返回 result 切片即可。
	return result
}

/*
   如果某个元素的 key 对应的不是 int 类型，则返回一个空的 []int 类型的切片。
*/ //MapListKeyToSliceInt 函数从输入的 []map[string]interface{} 类型的切片中提取指定 key 对应的 int 类型数值，并返回一个 []int 类型的切片。
func (s *toolSliceUtil) MapListKeyToSliceInt(list []map[string]interface{}, key string) []int {
	// 创建一个 []int 类型的切片 result，用于存储提取出来的 int 数值。
	result := make([]int, len(list))
	// 遍历输入的每个 map[string]interface{} 类型的数据记录 m，在其中找到 key 对应的值 v，
	// 如果 v 是 int 类型，则将其赋值给 result 切片对应的位置；否则，返回一个空的 []int 类型的切片。
	for i, m := range list {
		if v, ok := m[key]; ok {
			if num, ok := v.(int); ok {
				result[i] = num
			} else {
				return []int{}
			}
		} else {
			return []int{}
		}
	}
	// 返回 result 切片即可。
	return result
}

func (s *toolSliceUtil) MapListKeyToSliceStr(list []map[string]interface{}, key string) []string {
	// 创建一个 []int 类型的切片 result，用于存储提取出来的 int 数值。
	result := make([]string, len(list))
	// 遍历输入的每个 map[string]interface{} 类型的数据记录 m，在其中找到 key 对应的值 v，
	// 如果 v 是 int 类型，则将其赋值给 result 切片对应的位置；否则，返回一个空的 []int 类型的切片。
	for i, m := range list {
		if v, ok := m[key]; ok {
			if num, ok := v.(string); ok {
				result[i] = num
			} else {
				return []string{}
			}
		} else {
			return []string{}
		}
	}
	// 返回 result 切片即可。
	return result
}

// 移动第一位
func (s *toolSliceUtil) MoveToFirst(slice []map[string]interface{}, key string, value interface{}) {
	for i, item := range slice {
		if val, ok := item[key]; ok && val == value {
			// 将匹配到的元素移动到切片的第一位
			copy(slice[1:i+1], slice[:i])
			slice[0] = item
			break
		}
	}
}

// 分页
func (s *toolSliceUtil) Paginate(data []map[string]interface{}, pageNumber, itemsPerPage int) []map[string]interface{} {

	if len(data) == 0 {
		return nil
	}

	// 计算切片的总页数
	totalPages := (len(data) + itemsPerPage - 1) / itemsPerPage

	// 确保页码在有效范围内
	if pageNumber < 1 {
		pageNumber = 1
	} else if pageNumber > totalPages {
		pageNumber = totalPages
	}

	// 计算切片的起始索引和结束索引
	startIndex := (pageNumber - 1) * itemsPerPage
	endIndex := startIndex + itemsPerPage

	// 防止结束索引越界
	if endIndex > len(data) {
		endIndex = len(data)
	}

	// 返回分页数据
	return data[startIndex:endIndex]
}

// 删除指定key
func (s *toolSliceUtil) RemoveKeysFromMaps(slice []map[string]interface{}, keysToRemove []string) []map[string]interface{} {
	// 创建新的切片用于存储删除键后的结果
	updatedSlice := make([]map[string]interface{}, 0)

	// 遍历原始切片
	for _, item := range slice {
		// 创建新的 map 用于存储删除键后的元素
		updatedItem := make(map[string]interface{})

		// 复制原始 map 的键值对到新的 map
		for k, v := range item {
			// 检查当前键是否需要删除
			if !contains(keysToRemove, k) {
				updatedItem[k] = v
			}
		}

		// 将删除键后的元素添加到新的切片
		updatedSlice = append(updatedSlice, updatedItem)
	}

	return updatedSlice
}

func contains(slice []string, key string) bool {
	for _, v := range slice {
		if v == key {
			return true
		}
	}
	return false
}

func (s *toolSliceUtil) UniqueInt(slice []int) []int {
	seen := make(map[int]bool)
	result := []int{}

	for _, num := range slice {
		if !seen[num] {
			result = append(result, num)
			seen[num] = true
		}
	}

	return result
}

func (s *toolSliceUtil) UniqueString(slice []string) []string {
	seen := make(map[string]bool)
	result := []string{}

	for _, num := range slice {
		if !seen[num] {
			result = append(result, num)
			seen[num] = true
		}
	}

	return result
}

type SortField struct {
	Key   string
	IsAsc bool
}

func (s *toolSliceUtil) SortByKey(slice []map[string]interface{}, fields []SortField) {
	// 调用 sort.SliceStable 函数对 slice 进行排序，并提供一个函数作为比较器。
	sort.SliceStable(slice, func(i, j int) bool {
		for _, field := range fields {
			key := field.Key
			isAsc := field.IsAsc

			switch v1 := slice[i][key].(type) {
			case int:
				v2, _ := slice[j][key].(int)
				if v1 != v2 {
					if isAsc {
						return v1 < v2
					} else {
						return v1 > v2
					}
				}
			case int32:
				v2, _ := slice[j][key].(int32)
				if v1 != v2 {
					if isAsc {
						return v1 < v2
					} else {
						return v1 > v2
					}
				}
			case int64:
				v2, _ := slice[j][key].(int64)
				if v1 != v2 {
					if isAsc {
						return v1 < v2
					} else {
						return v1 > v2
					}
				}
			case float32:
				v2, _ := slice[j][key].(float32)
				if v1 != v2 {
					if isAsc {
						return v1 < v2
					} else {
						return v1 > v2
					}
				}
			case float64:
				v2, _ := slice[j][key].(float64)
				if v1 != v2 {
					if isAsc {
						return v1 < v2
					} else {
						return v1 > v2
					}
				}
			case string:
				v2, _ := slice[j][key].(string)
				if v1 != v2 {
					if isAsc {
						return v1 < v2
					} else {
						return v1 > v2
					}
				}
			default:
				fmt.Printf("unsupported type for key '%s': %s\n", key, reflect.TypeOf(v1))
				return false
			}
		}
		return false
	})
}

func compareJsonNumbers(a, b json.Number) bool {
	af, aErr := a.Float64()
	bf, bErr := b.Float64()
	if aErr != nil || bErr != nil {
		ai, aErr := a.Int64()
		bi, bErr := b.Int64()
		if aErr != nil || bErr != nil {
			fmt.Println("unsupported json.Number format")
			return false
		}
		return ai < bi
	}
	return af < bf
}

// StringToSliceInt 将字符串解析为Int切片类型
func (s *toolSliceUtil) StringToSliceInt(str string) []int {
	var slice []int
	err := json.Unmarshal([]byte(str), &slice)
	if err != nil {
		return []int{}
	}
	return slice
}

func (s *toolSliceUtil) StringToSliceStr(str string) []string {
	var slice []string
	err := json.Unmarshal([]byte(str), &slice)
	if err != nil {
		return []string{}
	}
	return slice
}

func (s *toolSliceUtil) ConvertStringSliceToIntSlice(stringSlice []string) []int {
	intSlice := make([]int, len(stringSlice))

	for i, str := range stringSlice {
		num, err := strconv.Atoi(str)
		if err != nil {
			return []int{} // 如果有错误，返回错误
		}
		intSlice[i] = num
	}

	return intSlice
}
